#include "XComTask.h"
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/bufferevent_ssl.h>
#include <iostream>
#include <cstring>
#include "XTools.h"
#include <mutex>
#include <thread>
#include "XSSL/XSSLCtx.h"
static void SReadCB(struct bufferevent *bev, void *ctx)
{
    XCOM_LOG_DEBUG("static void SReadCB");
    XComTask *pTask = (XComTask *)ctx;
    if (pTask)
    {
        pTask->readCB();
    }
}

static void SWriteCB(struct bufferevent *bev, void *ctx)
{
    XCOM_LOG_DEBUG("static void SWriteCB");
    XComTask *pTask = (XComTask *)ctx;
    if (pTask)
    {
        pTask->writeCB();
    }
}

static void SEventCB(struct bufferevent *bev, short events, void *ctx)
{
    XCOM_LOG_DEBUG("static void SEventCB");
    XComTask *pTask = (XComTask *)ctx;
    if (pTask)
    {
        pTask->eventCB(events);
    }
}

XComTask::XComTask()
    : m_mutex(new std::mutex())
{
}

XComTask::~XComTask()
{
    if (m_mutex)
    {
        XCOM_LOG_DEBUG("delete m_mutex");
        delete m_mutex;
        m_mutex = nullptr;
    }
}

/**
 * XComTask类中的connect方法，用于建立到服务器的连接
 * 此方法首先设置服务器地址和端口，然后尝试通过bufferevent建立socket连接
 *
 * @return bool 表示连接是否成功建立
 */
bool XComTask::connect()
{
    // 初始化服务器地址结构
    sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(m_servePort);
    // 将服务器IP地址转换为网络字节序
    int ret = evutil_inet_pton(AF_INET, m_serverIP, &addr.sin_addr.s_addr);
    if (ret == -1)
    {
        XCOM_LOG_ERROR("evutil_inet_pton connect error");
        return false;
    }
    // 锁定互斥量以保护共享资源
    std::unique_lock<std::mutex> lock(*m_mutex);

    // 初始化连接状态标志
    m_isConnected = false;
    m_isConnecting = false;
    // 如果bufferevent尚未初始化，则进行初始化
    if (!m_bev)
        ret = initBev(-1);
    if (!m_bev)
    {
        XCOM_LOG_ERROR("bufferevent_socket_new");
        return false;
    }

    // 尝试建立到服务器的连接
    if (0 != bufferevent_socket_connect(m_bev, (sockaddr *)&addr, sizeof(addr)))
    {
        XCOM_LOG_ERROR("bufferevent_socket_connect connect error");
        return false;
    }

    // 更新连接状态标志
    m_isConnecting = true;
    return true;
}
/**
 * @brief 初始化XComTask对象，设置通信套接字和事件回调。
 *
 * 该函数执行以下操作：
 * - 检查基础事件循环是否已创建。
 * - 获取并验证通信套接字的有效性。
 * - 创建一个新的缓冲事件对象，并设置读写回调和超时时间。
 * - 如果指定了服务器IP，则尝试连接到指定的服务器。
 *
 * @return bool 返回true表示初始化成功，返回false表示初始化失败。
 */
bool XComTask::Init()
{
    // 检查基础事件循环是否已经创建
    if (!base())
        return false;

    // 获取通信套接字
    evutil_socket_t comsock = sock();
    if (comsock <= 0)
        comsock = -1;
    // 锁定互斥锁以确保线程安全
    m_mutex->lock();
    bool ret = initBev(comsock);
    m_mutex->unlock();
    if (!ret)
        return false;
//     // 设置读写超时时间为10秒
// #if !defined(XCOM_DEBUG)
//     timeval tv = {10, 0};
//     bufferevent_set_timeouts(m_bev, &tv, &tv);
// #endif
    // 如果没有指定服务器IP，直接返回成功
    if (m_serverIP[0] == '\0')
        return true;
    // // 如果套接字无效，则设置为-1
    // if (sock() <= 0)
    //     comsock = -1;
    // 断开3秒后自动重连
    setAutoConnectTimer(3000);
    return connect();
}

// 关闭XCom任务
// 该函数负责释放XComTask实例中持有的资源，并删除实例本身
// 返回值为bool类型，但请注意，由于该函数始终返回true，其实际意义有限
bool XComTask::Close()
{
    m_mutex->lock();
    m_isConnected = false;
    m_isConnecting = false;
    // 释放bufferevent资源
    if (m_bev)
    {
        bufferevent_free(m_bev);
        // 将bufferevent指针置为nullptr，表示不再持有资源
        m_bev = nullptr;
    }
    // 删除当前对象实例
    m_mutex->unlock();
    // 始终返回true，表示关闭操作执行完毕
    if (m_deleteOnClose)
    {
        clearAllTimer();
        delete this;
    }
    return true;
}

// 等待连接建立的函数实现
// 参数 timeout_sec: 超时时间（秒），用于限制等待连接的时间
// 返回值: 如果在指定时间内成功建立连接，则返回true；否则返回false
bool XComTask::waitConnected(int timeout_sec)
{
    // 根据超时时间和监听间隔计算需要监听的次数
    // 10ms监听一次
    int count = timeout_sec * (1000 / 10);
    for (size_t i = 0; i < count; i++)
    {
        // 检查是否已经建立连接
        if (isConnected())
            return true;
        // 如果未连接，休眠10ms后再次检查
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    // 超时时间到达后，再次检查连接状态并返回
    return isConnected();
}

bool XComTask::autoConnect(int timeout_sec)
{
    try
    {
        if (isConnected())
        {
            return true;
        }
        if (timeout_sec <= 0)
        {
            // 处理无效的超时时间
            return false;
        }
        if (!isConnecting())
            if (!connect())
            {
                // 连接失败，直接返回
                return false;
            }
        return waitConnected(timeout_sec);
    }
    catch (const std::exception &e)
    {
        // 记录异常信息并返回失败
        std::cerr << "Exception in autoConnected: " << e.what() << std::endl;
        return false;
    }
}

/**
 * @brief 设置服务器IP地址
 *
 * 该函数用于更新或设置XComTask对象中保存的服务器IP地址
 * 它首先清空之前存储的IP地址，然后从参数中复制新的IP地址到成员变量
 *
 * @param ip 一个C风格字符串，表示新的服务器IP地址
 */
void XComTask::setServerIP(const char *ip)
{
    // 清空之前存储的IP地址
    memset(m_serverIP, 0, sizeof(m_serverIP));

    // 从参数中复制新的IP地址到成员变量
    memcpy(m_serverIP, ip, strlen(ip));
}
static void STimerCB(evutil_socket_t, short, void *arg)
{
    auto task = (XComTask *)arg;
    if (task)
        task->timerCB();
}
void XComTask::setTimer(int ms)
{
    if (!base())
    {
        XCOM_LOG_ERROR("setTimer is failed!base is null");
        return;
    }
    m_timerEvent = event_new(base(), -1, EV_PERSIST, STimerCB, this);
    if (!m_timerEvent)
    {
        XCOM_LOG_ERROR("setTimer is failed!timer_event is null");
        return;
    }
    timeval tv;
    tv.tv_sec = ms / 1000;
    tv.tv_usec = (ms % 1000) * 1000;
    event_add(m_timerEvent, &tv);
}

void XComTask::clearTimer()
{
    if (m_timerEvent)
    {
        event_del(m_timerEvent);
        event_free(m_timerEvent);
        m_timerEvent = nullptr;
    }
}

static void SAutoConnectTimerCB(evutil_socket_t, short, void *arg)
{
    auto task = (XComTask *)arg;
    if (task)
        task->autoConnectTimerCB();
}

void XComTask::setAutoConnectTimer(int ms)
{
    if (!base())
    {
        XCOM_LOG_ERROR("setTimer is failed!base is null");
        return;
    }
    if (m_autoConnectTimerEvent)
    {
        event_del(m_autoConnectTimerEvent);
        event_free(m_autoConnectTimerEvent);
        m_autoConnectTimerEvent = nullptr;
    }
    m_autoConnectTimerEvent = event_new(base(), -1, EV_PERSIST, SAutoConnectTimerCB, this);
    if (!m_autoConnectTimerEvent)
    {
        XCOM_LOG_ERROR("setTimer is failed!timer_event is null");
        return;
    }
    timeval tv;
    tv.tv_sec = ms / 1000;
    tv.tv_usec = (ms % 1000) * 1000;
    event_add(m_autoConnectTimerEvent, &tv);
}

void XComTask::autoConnectTimerCB()
{
    if (!base())
    {
        XCOM_LOG_ERROR("autoConnectTimerCB is failed!base is null");
        return;
    }
    if(!m_autoConnect){
        return;
    }
    std::cout << ".";
    if (isConnected())
    {
        return;
    }
    if (!isConnecting())
        connect();
}

void XComTask::clearAutoConnectTimer()
{
    if (m_autoConnectTimerEvent)
    {
        event_del(m_autoConnectTimerEvent);
        event_free(m_autoConnectTimerEvent);
        m_autoConnectTimerEvent = nullptr;
    }
}
void XComTask::clearAllTimer()
{
    if (m_autoConnectTimerEvent)
    {
        event_del(m_autoConnectTimerEvent);
        event_free(m_autoConnectTimerEvent);
        m_autoConnectTimerEvent = nullptr;
    }
    if (m_timerEvent)
    {
        event_del(m_timerEvent);
        event_free(m_timerEvent);
        m_timerEvent = nullptr;
    }
}
/**
 * 初始化通信socket
 *
 * @param comsock 通信套接字描述符
 *
 * @return 成功返回true，失败返回false
 *
 * 此函数负责初始化一个bufferevent对象，用于处理通信套接字上的事件
 * 它首先锁定互斥锁以确保线程安全，然后尝试创建一个新的bufferevent对象
 * 如果创建失败，记录错误信息并返回false
 * 成功创建后，设置bufferevent的回调函数，用于处理读、写和事件
 * 最后，启用bufferevent的读写事件处理功能
 * 如果启用事件处理成功，函数返回true，否则返回false
 */
bool XComTask::initBev(evutil_socket_t comsock)
{
    // 创建一个新的bufferevent对象，用于处理通信套接字上的事件
    // 判断是否使用ssl
    if (!sslCtx())
    {
        m_bev = bufferevent_socket_new(base(), comsock, BEV_OPT_CLOSE_ON_FREE);
    }
    else
    {
        XSSL xssl = sslCtx()->NewXSSL(comsock);
        if (comsock < 0)
        { // 客户端
            m_bev = bufferevent_openssl_socket_new(base(), comsock, xssl.ssl(), BUFFEREVENT_SSL_CONNECTING, BEV_OPT_CLOSE_ON_FREE);
        }
        else
        { // 服务端
            m_bev = bufferevent_openssl_socket_new(base(), comsock, xssl.ssl(), BUFFEREVENT_SSL_ACCEPTING, BEV_OPT_CLOSE_ON_FREE);
        }
    }
    if (!m_bev)
    {
        XCOM_LOG_ERROR("bufferevent_socket_new failed");
        return false;
    }
    // 设置读超时
    if (read_timeout_ms_ > 0)
    {
        timeval time = {
            .tv_sec = read_timeout_ms_ / 1000,
            .tv_usec = (read_timeout_ms_ % 1000) * 1000};
        bufferevent_set_timeouts(m_bev, &time, nullptr);
    }
    // 设置bufferevent的回调函数，用于处理读、写和事件
    bufferevent_setcb(m_bev, SReadCB, SWriteCB, SEventCB, this);

    // 启用bufferevent的读写事件处理功能
    int ret = bufferevent_enable(m_bev, EV_READ | EV_WRITE);

    // 如果启用事件处理成功，返回true，否则返回false
    return ret == 0;
}

/**
 * XComTask类的事件回调函数，用于处理不同的网络事件
 * @param events 触发的事件类型，可以是多个事件的位组合
 */
void XComTask::eventCB(short events)
{
    // 当检测到连接成功事件时
    if (events & BEV_EVENT_CONNECTED)
    {
        // 输出连接成功的日志信息
        XCOM_LOG_INFO("connect server " << m_serverIP << ":" << m_servePort);
        m_isConnecting = false;
        m_isConnected = true;
        auto ss = bufferevent_openssl_get_ssl(m_bev);
        if (ss)
        {
            XSSL xssl;
            xssl.set_ssl_(ss);
            xssl.PrintCipher();
            xssl.PrintPeerCert();
        }
        // 调用连接成功后的回调函数
        connectedCB();
    }
    else if (events & BEV_EVENT_ERROR)
    {
        ssl_st *ssl = bufferevent_openssl_get_ssl(m_bev);
        if (ssl)
        {
            XSSL xssl;
            xssl.set_ssl_(ssl);
            xssl.PrintCipher();
        }
        evutil_socket_t sock = bufferevent_getfd(m_bev);
        int err = evutil_socket_geterror(sock);
        XCOM_LOG_ERROR("XComTask::eventCB(short events) error:" << evutil_socket_error_to_string(err));
        Close();
    }
    // 当检测到错误事件或超时事件时
    else if (events & BEV_EVENT_TIMEOUT)
    {
        // 输出超时或错误的日志信息
        XCOM_LOG_ERROR("XComTask::eventCB(short events) timeout");
        // 调用关闭连接的函数
        Close();
    }
    // 当检测到文件结束（EOF）事件时
    else if (events & BEV_EVENT_EOF)
    {
        // 输出EOF的日志信息
        XCOM_LOG_ERROR("XComTask::eventCB(short events) eof");
        // 调用关闭连接的函数
        Close();
    }
}

void XComTask::connectedCB()
{
}

/**
 * 从bufferevent对象中读取数据
 *
 * @param data 存储读取数据的缓冲区指针
 * @param datasize 缓冲区的大小，指示可以读取的最大数据量
 * @return 读取的数据量，如果发生错误或达到文件末尾可能返回特殊值
 *
 * 此函数尝试从m_bev指向的bufferevent对象中读取数据如果m_bev未被正确设置，
 * 函数将记录错误信息并返回0，表示未进行读取操作这是为了确保m_bev是有效且准备就绪的
 *
 * 注意：此函数的实现假定m_bev成员变量和bufferevent_read函数的定义和行为
 */
int XComTask::Read(void *data, size_t datasize)
{
    // 检查m_bev是否未被设置如果是，则记录错误信息并返回0
    if (!m_bev)
    {
        XCOM_LOG_ERROR("m_bev not set!");
        return 0;
    }

    // 使用bufferevent_read尝试从m_bev中读取数据返回实际读取的数据量
    int ret = bufferevent_read(m_bev, data, datasize);
    return ret;
}

/**
 * @brief 写入数据到bufferevent对象。
 *
 * 此函数将给定的数据块写入到m_bev指向的bufferevent对象中。它会检查m_bev、数据指针和数据大小的有效性，
 * 并调用bufferevent_write进行实际写入操作。如果任何检查失败或写入操作失败，函数将返回false。
 *
 * @param data 数据块的指针。
 * @param size 数据块的大小（以字节为单位）。
 * @return 成功返回true，失败返回false。
 */
bool XComTask::write(const void *data, size_t size)
{
    std::unique_lock<std::mutex> lock(*m_mutex);
    if (!m_bev || !data || size < 0)
        return false;
    if (bufferevent_write(m_bev, data, size) != 0)
        return false;
    return true;
}

void XComTask::BeginWrite()
{
    if (!m_bev)
        return;
    // 触发bufferevent的写事件
    bufferevent_trigger(m_bev, EV_WRITE, 0);
}